package com.gxjtkyy.datadistributer.writer.rdbms.service;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.gxjtkyy.datadistributer.common.dto.DbRecord;
import com.gxjtkyy.datadistributer.common.dto.XField;
import com.gxjtkyy.datadistributer.common.util.FieldParserUtil;
import com.gxjtkyy.datadistributer.writer.rdbms.util.FieldParserNewUtil;
import com.gxjtkyy.datadistributer.writer.rdbms.util.TableColumnsUtil;
import com.gxjtkyy.datadistributer.writer.rdbms.vo.DbBase;
import com.gxjtkyy.datadistributer.writer.rdbms.vo.DbTableSetting;

import java.util.*;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

@Service
@ConditionalOnProperty(
   value = {"db-writer.type"},
   havingValue = "1"
)
public class DbWriterService extends AbstractDbWriterService<DbRecord> {
   private static final Logger log = LoggerFactory.getLogger(DbWriterService.class);
   private final DbWriterConfigService writerConfigService;
   private final ObjectMapper objectMapper;
   private final DbRecordFilterService dbRecordFilterService;
   private final TableColumnsUtil tableColumnsUtil;

   public DbWriterService(ObjectMapper objectMapper, JdbcTemplate jdbcTemplate, DbWriterConfigService writerConfigService, DbRecordFilterService dbRecordFilterService,
                          TableColumnsUtil tableColumnsUtil) {
      super(jdbcTemplate);
      this.objectMapper = objectMapper;
      this.writerConfigService = writerConfigService;
      this.dbRecordFilterService = dbRecordFilterService;
      this.tableColumnsUtil = tableColumnsUtil;
   }

   public DbBase getDbBase(DbRecord dbRecord) {
      //校验表名，是否存在主键，字段是否更新，是否插入等
      DbTableSetting dbTableSetting = this.writerConfigService.getDbTableSetting(dbRecord.getTable());
//      Map<Integer, XField> xFieldMap = dbRecord.getFields().stream().filter((xField) -> {
//         return FieldParserUtil.parseColumnValue(xField.getType(), xField.getEncode(), xField.getValue()) != null || dbTableSetting.isKeyColumn(xField.getName());
//      }).map((field) -> {
//         int key = field.getIdx();
//         return Map.entry(key, field);
//      }).collect(Collectors.toMap(Entry::getKey, Entry::getValue));
      //20241017修改
      Map<Integer, XField> xFieldMap = dbRecord.getFields().stream().filter((xField) -> {
         return FieldParserUtil.parseColumnValue(xField.getType(), xField.getEncode(), xField.getValue()) != null || dbTableSetting.isKeyColumn(xField.getName());
      }).collect(Collectors.toMap(XField::getIdx,xField -> xField));
      //获取主键字段
      List<Integer> keyIndexes = getKeyIndexes(xFieldMap, dbTableSetting);
      //非主键字段
      List<Integer> otherIndexes = getOtherIndexes(xFieldMap, dbTableSetting);
//      Stream var10000 = keyIndexes.stream();
      Objects.requireNonNull(xFieldMap);
      List<String> keyColumnNames = keyIndexes.stream().map(xFieldMap::get).map(XField::getName).collect(Collectors.toList());
//      var10000 = otherIndexes.stream();
      Objects.requireNonNull(xFieldMap);
      List<String> otherColumnNames = otherIndexes.stream().map(xFieldMap::get).map(XField::getName).collect(Collectors.toList());
      boolean updatable = dbTableSetting.isUpdatable();
      //不是主键的字段，并且不在ignoredColumnsForUpdate列表里，（generateInsertDbOperation里又没用到updateValues）
      List<Integer> updateIndexes = getUpdateIndexes(xFieldMap, dbTableSetting, updatable);
//      var10000 = updateIndexes.stream();
      Objects.requireNonNull(xFieldMap);
      List<String> updateColumnNames = updateIndexes.stream().map(xFieldMap::get).map(XField::getName).collect(Collectors.toList());
      String targetTableName = dbTableSetting.getTargetTableName();

      List<Object> keyValues = this.parseColumnValues(keyIndexes, xFieldMap, dbTableSetting);
      List<Object> otherValues = this.parseColumnValues(otherIndexes, xFieldMap, dbTableSetting);
      List<Object> updateValues = this.parseColumnValues(updateIndexes, xFieldMap, dbTableSetting);
      DbBase dbBase = new DbBase();
      dbBase.setTargetTableName(targetTableName);
      dbBase.setKeyColumnNames(keyColumnNames);
      dbBase.setOtherColumnNames(otherColumnNames);
      dbBase.setUpdateColumnNames(updateColumnNames);
      dbBase.setComparableColumns(dbTableSetting.getComparableColumns());
      dbBase.setInsertable(true);
      dbBase.setUpdatable(updatable);
      dbBase.setKeyValues(keyValues);
      dbBase.setOtherValues(otherValues);
      dbBase.setUpdateValues(updateValues);
      dbBase.setDropWhenValueTooLarge(dbTableSetting.isDropWhenValueTooLarge());
      dbBase.setDropWhenCannotInsertNull(dbTableSetting.isDropWhenCannotInsertNull());
      dbBase.setDropWhenValueLargeThanSpecifiedPrecision(dbTableSetting.isDropWhenValueLargeThanSpecifiedPrecision());
      return dbBase;
   }

   private boolean isNumber(String columnName, Map<String, String> columnNameMap) {
      String realColumnName = columnName;
      if (!columnNameMap.containsKey(columnName)) {
         for (String key : columnNameMap.keySet()) {
            if (key.equalsIgnoreCase(columnName)) {
               realColumnName  = key;
               break;
            }
         }
      }
      String dataType = columnNameMap.get(realColumnName);
      if (dataType.contains("int") || dataType.contains("float")) {
         return true;
      }
      return false;
   }

   protected boolean isIgnored(DbRecord dbRecord) {
      String tableName = dbRecord.getTable();
      return dbRecord.getFields().stream().anyMatch((xField) -> {
         return this.dbRecordFilterService.isIgnoredByColumn(tableName, xField.getName(), xField.getValue());
      });
   }

   private static List<Integer> getUpdateIndexes(Map<Integer, XField> xFieldMap, DbTableSetting dbTableSetting, boolean updatable) {
      List updateIndexes;
      if (updatable) {
         updateIndexes = (List)xFieldMap.entrySet().stream().filter((entry) -> {
            return !((XField)entry.getValue()).isKey();
         }).filter((entry) -> {
            String columnName = ((XField)entry.getValue()).getName();
            return !dbTableSetting.isIgnoredColumnForUpdate(columnName) && !dbTableSetting.isKeyColumn(columnName);
         }).map(Entry::getKey).sorted().collect(Collectors.toList());
      } else {
         updateIndexes = Collections.emptyList();
      }

      return updateIndexes;
   }

   private static List<Integer> getOtherIndexes(Map<Integer, XField> xFieldMap, DbTableSetting dbTableSetting) {
      return (List)xFieldMap.entrySet().stream().filter((entry) -> {
         String columnName = ((XField)entry.getValue()).getName();
         return !dbTableSetting.isIgnoredColumnForInsert(columnName) && !dbTableSetting.isKeyColumn(columnName);
      }).map(Entry::getKey).sorted().collect(Collectors.toList());
   }

   private static List<Integer> getKeyIndexes(Map<Integer, XField> xFieldMap, DbTableSetting dbTableSetting) {
      return (List)xFieldMap.entrySet().stream().filter((entry) -> {
         String columnName = ((XField)entry.getValue()).getName();
         return dbTableSetting.isKeyColumn(columnName);
      }).map(Entry::getKey).sorted().collect(Collectors.toList());
   }

   protected DbRecord parseOriginalRecord(String originalRecord) {
      try {
         return (DbRecord)this.objectMapper.readValue(originalRecord, DbRecord.class);
      } catch (JsonProcessingException e) {
         // 增加异常日志记录
         log.error("Failed to parseOriginalRecord: " + originalRecord, e);
         // 可以选择抛出自定义异常或重新抛出原始异常
         throw new RuntimeException("Failed to pparseOriginalRecord", e);
      }
   }

   private List<Object> parseColumnValues(List<Integer> indexes, Map<Integer, XField> xFieldMap, DbTableSetting dbTableSetting) {
      //获取表的字段结构
      Map<String, String> columnNameMap = tableColumnsUtil.columnNameMap.get(dbTableSetting.getTargetTableName());
      return indexes.stream().map((index) -> {
         XField xField = xFieldMap.get(index);
         String columnName = xField.getName();
         String value = dbTableSetting.getValue(columnName, xField.getValue());
         boolean isNumber = isNumber(columnName, columnNameMap);
         return FieldParserNewUtil.parseColumnValue(xField.getType(), xField.getEncode(), value, isNumber);
      }).collect(Collectors.toList());
   }
}
